package com.kapcb.schedule.client;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.kapcb.schedule.bean.XxlJobGroup;
import com.kapcb.schedule.bean.XxlJobInfo;
import com.kapcb.schedule.constant.XxlJobAdminConstant;
import com.kapcb.schedule.properties.XxlJobProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.HttpCookie;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * <a>Title: XxlJobAdminHttpClient </a>
 * <a>Author: kapcb <a>
 * <a>Description: XxlJobAdminHttpClient <a>
 *
 * @author Kpacb
 * @version 1.0
 * @date 2024/5/28 21:58
 * @since 1.0
 */
@Slf4j
public class XxlJobAdminHttpClient {

    private final XxlJobProperties xxlJobProperties;

    private static final Map<String, String> LOGIN_COOKIES = new ConcurrentHashMap<>();

    public XxlJobAdminHttpClient(XxlJobProperties xxlJobProperties) {
        this.xxlJobProperties = xxlJobProperties;
    }

    public String getCookie() {
        // 为了避免这一过程失败，允许最多重试 3 次
        for (int i = 0; i < 3; i++) {
            String cookieStr = LOGIN_COOKIES.get(XxlJobAdminConstant.LOGIN_COOKIE_NAME);
            if (StringUtils.isNotBlank(cookieStr)) {
                return XxlJobAdminConstant.LOGIN_COOKIE_NAME + "=" + cookieStr;
            }
            login();
        }
        throw new RuntimeException("get xxl-job cookie error!");
    }

    /**
     * 通过 xxl-job admin 的登录接口获取 cookie，并在获取到 cookie 后，缓存到本地的 Map
     */
    public void login() {
        String url = xxlJobProperties.getAdminAddresses() + XxlJobAdminConstant.API_ADMIN_LOGIN;

        HttpResponse httpResponse;
        try {
            httpResponse = HttpRequest.post(url)
                    .form("userName", xxlJobProperties.getUsername())
                    .form("password", xxlJobProperties.getPassword())
                    .execute();
        } catch (Exception e) {
            throw new RuntimeException("login to xxl job admin error", e);
        }

        Optional<HttpCookie> cookieOptional = httpResponse.getCookies().stream().filter(cookie -> cookie.getName().equals(XxlJobAdminConstant.LOGIN_COOKIE_NAME)).findFirst();
        if (!cookieOptional.isPresent())
            throw new RuntimeException("get xxl-job cookie error!");

        String cookie = cookieOptional.get().getValue();
        LOGIN_COOKIES.put(XxlJobAdminConstant.LOGIN_COOKIE_NAME, cookie);
    }


    /**
     * 根据 appName 和执行器名称 title 查询执行器列表
     *
     * @return
     */
    public List<XxlJobGroup> getJobGroup() {
        String url = xxlJobProperties.getAdminAddresses() + XxlJobAdminConstant.API_JOB_GROUP_LIST;

        HttpResponse httpResponse;
        try {
            httpResponse = HttpRequest.post(url)
                    .form("appname", xxlJobProperties.getAppName())
                    .form("title", xxlJobProperties.getTitle())
                    .cookie(getCookie())
                    .execute();
        } catch (Exception e) {
            throw new RuntimeException("get job group error", e);
        }

        String body = httpResponse.body();
        JSONArray array = JSONUtil.parse(body).getByPath("data", JSONArray.class);

        return array.stream()
                .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobGroup.class))
                .collect(Collectors.toList());
    }

    // https://blog.csdn.net/zlfjavahome/article/details/131403419?ops_request_misc=&request_id=&biz_id=102&utm_term=xxl%20job%20%E4%BB%BB%E5%8A%A1%E8%87%AA%E5%8A%A8%E6%B3%A8%E5%86%8C&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-131403419.142^v100^pc_search_result_base4&spm=1018.2226.3001.4187


    /**
     * 根据配置文件中的 appName 和 title 判断当前执行器是否已经被注册到调度中心过，如果已经注册过那么则跳过，
     * 而 /jobgroup/pageList 接口是一个模糊查询接口，所以在查询列表的结果列表中，还需要再进行一次精确匹配
     *
     * @return
     */
    public boolean preciselyCheck() {
        List<XxlJobGroup> jobGroup = getJobGroup();
        if (CollectionUtils.isEmpty(jobGroup)) {
            return false;
        }
        Optional<XxlJobGroup> has = jobGroup.stream()
                .filter(xxlJobGroup -> xxlJobGroup.getAppname().equals(xxlJobProperties.getAdminAddresses()) && xxlJobGroup.getTitle().equals(xxlJobProperties.getTitle())).findAny();
        return has.isPresent();
    }

    /**
     * 注册 executor 到调度中心
     *
     * @return
     */
    public boolean registerGroup() {
        String url = xxlJobProperties.getAdminAddresses() + XxlJobAdminConstant.API_JOB_GROUP_SAVE;
        HttpResponse response = HttpRequest.post(url)
                .form("appname", xxlJobProperties.getAppName())
                .form("title", xxlJobProperties.getTitle())
                .cookie(getCookie())
                .execute();
        Object code = JSONUtil.parse(response.body()).getByPath("code");
        return code.equals(200);
    }

    /**
     * 根据执行器id，jobHandler名称查询任务列表，和上面一样，也是模糊查询
     *
     * @param jobGroupId
     * @param executorHandler
     * @return
     */
    public List<XxlJobInfo> getJobInfo(Integer jobGroupId, String executorHandler) {
        String url = xxlJobProperties.getAdminAddresses() + XxlJobAdminConstant.API_JOB_INFO_LIST;
        HttpResponse response = HttpRequest.post(url)
                .form("jobGroup", jobGroupId)
                .form("executorHandler", executorHandler)
                .form("triggerStatus", -1)
                .cookie(getCookie())
                .execute();

        String body = response.body();
        JSONArray array = JSONUtil.parse(body).getByPath("data", JSONArray.class);
        List<XxlJobInfo> list = array.stream()
                .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobInfo.class))
                .collect(Collectors.toList());

        return list;
    }

    /**
     * 注册一个新任务，最终返回创建的新任务的id
     *
     * @param xxlJobInfo
     * @return
     */
    public Integer addJobInfo(XxlJobInfo xxlJobInfo) {
        String url = xxlJobProperties.getAdminAddresses() + XxlJobAdminConstant.API_JOB_INFO_ADD;
        Map<String, Object> paramMap = BeanUtil.beanToMap(xxlJobInfo);
        HttpResponse response = HttpRequest.post(url)
                .form(paramMap)
                .cookie(getCookie())
                .execute();

        JSON json = JSONUtil.parse(response.body());
        Object code = json.getByPath("code");
        if (code.equals(200)) {
            return Convert.toInt(json.getByPath("content"));
        }
        throw new RuntimeException("add jobInfo error!");
    }


}
